package cn.mengfly.testnrz.im.client;

import cn.mengfly.testnrz.im.client.bean.User;
import cn.mengfly.testnrz.im.client.command.*;
import cn.mengfly.testnrz.im.client.sender.ChatSender;
import cn.mengfly.testnrz.im.client.sender.LoginSender;
import cn.mengfly.testnrz.nio.NioDemoConfig;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoop;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.Data;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Data
public class CommandClient {

    private static CommandClient mInstance;

    public CommandClient() {
        mInstance = this;
    }

    private boolean connectFlag = false;

    private ClientSession session;

    private Map<String, BaseCommand> commandMap;
    private String menuString;
    private Channel channel;


    ClientCommandMenu clientCommandMenu = new ClientCommandMenu();
    /**
     * sender
     */
    ChatSender chatSender = new ChatSender();
    LoginSender loginSender = new LoginSender();
    NettyClient client = new NettyClient(NioDemoConfig.SOCKET_IP, NioDemoConfig.SOCKET_PORT);

    GenericFutureListener<ChannelFuture> closeListener = new GenericFutureListener<ChannelFuture>() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            System.out.println(new Date() + "连接已断开...");
            channel = future.channel();

            // 创建会话
            ClientSession session = channel.attr(ClientSession.SESSION_KEY).get();
            session.close();

            // 唤醒用户线程
            notifyCommandThread();
        }
    };

    private synchronized void notifyCommandThread() {
        this.notify();
    }

    GenericFutureListener<ChannelFuture> connectedListener = new GenericFutureListener<ChannelFuture>() {
        @Override
        public void operationComplete(ChannelFuture future) {
            EventLoop eventLoop = future.channel().eventLoop();
            if (!future.isSuccess()) {
                System.out.println("连接失败，在10秒后准备重试连接");
                eventLoop.schedule(new Runnable() {
                    @Override
                    public void run() {
                        client.doConnect();
                    }
                }, 10, TimeUnit.SECONDS);
                connectFlag = false;
            } else {
                connectFlag = true;
                System.out.println("服务器【IM】连接成功");
                channel = future.channel();
                // 创建会话
                session = new ClientSession(channel);
                session.setConnected(true);
                channel.closeFuture().addListener(closeListener);
                notifyCommandThread();
            }
        }
    };

    private User user;

    public static void reconnect() {
        mInstance.setConnectFlag(false);
        mInstance.startConnectServer();
    }

    private void startConnectServer() {
        FutureTaskSchedule.add(new ExecuteTask() {
            @Override
            public void execute() {
                client.setConnectedListener(connectedListener);
                client.doConnect();
            }
        });
    }

    public void startCommandThread() throws InterruptedException {
        Thread.currentThread().setName("主线程");
        while (true) {
            // 建立连接
            while (!connectFlag) {
                // 开始连接
                startConnectServer();
                waitCommandThread();
            }
            while (null != session && session.isConnected()) {
                Scanner scanner = new Scanner(System.in);
                clientCommandMenu.exec(scanner);
                String key = clientCommandMenu.getCommandInput();
                BaseCommand command = commandMap.get(key);
                if (null == command) {
                    System.out.println("无法识别[" + key + "]指令，请重新输入！");
                    continue;
                }
                command.exec(scanner);
                switch (command.getKey()) {
                    case ChatConsoleCommand.KEY:
                        startOneChat((ChatConsoleCommand) command);
                        break;
                    case LoginConsoleCommand.KEY:
                        startLogin((LoginConsoleCommand) command);
                        break;
                    case LogoutConsoleCommand.KEY:
                        startLogout((LogoutConsoleCommand) command);
                        break;
                    default:
                        System.out.println("无法识别[" + key + "] 命令");
                }
            }
        }
    }

    /**
     * 休眠当前线程
     */
    private synchronized void waitCommandThread() {
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void startLogout(LogoutConsoleCommand command) {
        if (!isLogin()) {
            System.out.println("还没有登录，请先登录");
            return;
        }
        // TODO  2021/1/31  logout
    }

    private void startLogin(LoginConsoleCommand command) {
        if (!isConnectFlag()) {
            System.out.println("连接异常，请重新建立连接");
            return;
        }
        User user = new User();
        user.setUid(command.getUserName());
        user.setToken(command.getPassword());
        user.setDeviceId("1111");
        this.user = user;
        session.setUser(user);
        loginSender.setUser(user);
        loginSender.setSession(session);
        loginSender.sendLoginMsg();
    }

    private void startOneChat(ChatConsoleCommand command) {
        if (!isLogin()) {
            System.out.println("还没有登录，请先登录");
            return;
        }
        chatSender.setSession(session);
        chatSender.setUser(user);
        chatSender.sendChatMsg(command.getToUserId(), command.getMessage());
    }

    public boolean isLogin() {
        if (null == session) {
            System.out.println("session is null");
            return false;
        }
        return session.isLogin();
    }

    public void initCommandMap() {
        commandMap = new HashMap<>();

        commandMap.put(clientCommandMenu.getKey(), clientCommandMenu);
        BaseCommand chatConsoleCommand = new ChatConsoleCommand();
        commandMap.put(chatConsoleCommand.getKey(), chatConsoleCommand);
        BaseCommand loginConsoleCommand = new LoginConsoleCommand();
        commandMap.put(loginConsoleCommand.getKey(), loginConsoleCommand);
        BaseCommand logoutConsoleCommand = new LogoutConsoleCommand();
        commandMap.put(logoutConsoleCommand.getKey(), logoutConsoleCommand);

        StringBuilder menus = new StringBuilder();
        Collection<BaseCommand> values = commandMap.values();
        for (BaseCommand value : values) {
            menus.append(value.getKey()).append("->").append(value.getTip()).append("|");
        }
        menuString = menus.toString();
        clientCommandMenu.setAllCommandShow(menuString);
    }
}
